<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="bFi8l" id="bFi8l"><span data-lake-id="uefe4bf53" id="uefe4bf53">典型回答</span></h1>
  <p data-lake-id="u173a074c" id="u173a074c"><span data-lake-id="ubbd2dbce" id="ubbd2dbce">kill -9 命令会立刻关闭Jvm进程。但是kill -9的语意是强制关闭，会导致在Jvm中执行的服务立刻关闭，来不及收尾。如导致RPC服务没有从注册中心取消注册导致服务不可用，如导致事务执行一半直接终止等等</span></p>
  <h2 data-lake-id="uSKFa" id="uSKFa"><span data-lake-id="u61f812d8" id="u61f812d8">kill 命令</span></h2>
  <p data-lake-id="u431315dd" id="u431315dd"><span data-lake-id="ucb1e70e8" id="ucb1e70e8">我们都知道，想要</span><span data-lake-id="u95c8f1ec" id="u95c8f1ec">在Linux中终止一个进程有两种方式，如果是前台进程可以使用Ctrl+C键进行终止；如果是后台进程，那么需要使用kill命令来终止。</span><span data-lake-id="u1724bf3f" id="u1724bf3f">（其实Ctrl+C也是kill命令）</span></p>
  <p data-lake-id="u5cb50406" id="u5cb50406"><span data-lake-id="u5f6aa5a8" id="u5f6aa5a8">kill命令的格式是：</span></p>
  <blockquote data-lake-id="u06311d9a" id="u06311d9a">
   <p data-lake-id="ueece5938" id="ueece5938"><span data-lake-id="u7887dfa4" id="u7887dfa4">kill[参数] [进程号] ，如：</span></p>
   <p data-lake-id="u9c633c49" id="u9c633c49"><span data-lake-id="u7bbb4602" id="u7bbb4602">kill 21121 </span></p>
   <p data-lake-id="u1d02256c" id="u1d02256c"><span data-lake-id="u5adb99e7" id="u5adb99e7">kill -9 21121</span></p>
  </blockquote>
  <p data-lake-id="u40d38579" id="u40d38579"><span data-lake-id="uff810f0d" id="uff810f0d">其中[参数]是可选的，进程号可以通过jps/ps/pidof/pstree/top等工具获取。</span></p>
  <p data-lake-id="ucc567b9f" id="ucc567b9f"><span data-lake-id="ud94ed8ec" id="ud94ed8ec">kill的命令参数有以下几种：</span></p>
  <blockquote data-lake-id="uc029323d" id="uc029323d">
   <p data-lake-id="u750d3139" id="u750d3139"><span data-lake-id="u1e257fbd" id="u1e257fbd">-l 信号，如果不加信号的编号参数，则使用“-l”参数会列出全部的信号名称</span></p>
   <p data-lake-id="ufec1a90c" id="ufec1a90c"><span data-lake-id="u66f9108d" id="u66f9108d">-a 当处理当前进程时，不限制命令名和进程号的对应关系</span></p>
   <p data-lake-id="u3b93f9d9" id="u3b93f9d9"><span data-lake-id="u8f62085b" id="u8f62085b">-p 指定kill 命令只打印相关进程的进程号，而不发送任何信号</span></p>
   <p data-lake-id="u5140ec65" id="u5140ec65"><span data-lake-id="u8ee0c700" id="u8ee0c700">-s 指定发送信号</span></p>
   <p data-lake-id="uf4817e78" id="uf4817e78"><span data-lake-id="u0d4898e6" id="u0d4898e6">-u 指定用户</span></p>
  </blockquote>
  <p data-lake-id="uc2304ece" id="uc2304ece"><span data-lake-id="u66304606" id="u66304606">通常情况下，我们使用的-l(信号)的时候比较多，如我们前文提到的</span><code data-lake-id="ue89b5ae7" id="ue89b5ae7"><span data-lake-id="ueff552f6" id="ueff552f6">kill -9</span></code><span data-lake-id="u8a3d1fea" id="u8a3d1fea">中的9就是信号。</span></p>
  <p data-lake-id="ucd0b719e" id="ucd0b719e"><span data-lake-id="uce75e1e7" id="uce75e1e7">信号如果没有指定的话，默认会发出终止信号(15)。常用的信号如下：</span></p>
  <blockquote data-lake-id="u94d11e6e" id="u94d11e6e">
   <p data-lake-id="u7bd682dd" id="u7bd682dd"><span data-lake-id="u3c5c8c14" id="u3c5c8c14">HUP 1 终端断线</span></p>
   <p data-lake-id="u6a98d11b" id="u6a98d11b"><span data-lake-id="uaaf85a1b" id="uaaf85a1b">INT 2 中断（同 Ctrl + C）</span></p>
   <p data-lake-id="u19c70a85" id="u19c70a85"><span data-lake-id="ufd02501d" id="ufd02501d">QUIT 3 退出（同 Ctrl + \）</span></p>
   <p data-lake-id="ufc593466" id="ufc593466"><span data-lake-id="u29765c55" id="u29765c55">TERM 15 终止</span></p>
   <p data-lake-id="ub3906e30" id="ub3906e30"><span data-lake-id="uc77ba526" id="uc77ba526">KILL 9 强制终止</span></p>
   <p data-lake-id="u02626541" id="u02626541"><span data-lake-id="uae2f26d0" id="uae2f26d0">CONT 18 继续（与STOP相反， fg/bg命令）</span></p>
   <p data-lake-id="ua2770363" id="ua2770363"><span data-lake-id="u0c769a6f" id="u0c769a6f">STOP 19 暂停（同 Ctrl + Z）</span></p>
  </blockquote>
  <p data-lake-id="u4f3149f1" id="u4f3149f1"><span data-lake-id="u5b6149c7" id="u5b6149c7">比较常用的就是强制终止信号：9和终止信号：15。</span></p>
  <p data-lake-id="u93ddef8c" id="u93ddef8c"><span data-lake-id="u1e395568" id="u1e395568">另外，中断信号：2其实就是我们前文提到的Ctrl + C结束前台进程。</span></p>
  <p data-lake-id="ua259768d" id="ua259768d"><span data-lake-id="u1faf08ac" id="u1faf08ac">那么，</span><code data-lake-id="ubc222e92" id="ubc222e92"><span data-lake-id="u95e1d4cf" id="u95e1d4cf">kill -9</span></code><span data-lake-id="u9db68f97" id="u9db68f97">和</span><code data-lake-id="u86df4f4d" id="u86df4f4d"><span data-lake-id="u6925fcd1" id="u6925fcd1">kill -15</span></code><span data-lake-id="ua7c27044" id="ua7c27044">到底有什么区别呢？该如何选择呢？</span></p>
  <h2 data-lake-id="kLSX6" id="kLSX6"><span data-lake-id="u529ad24b" id="u529ad24b">kill -9 和 kill -15的区别</span></h2>
  <p data-lake-id="u48bf941e" id="u48bf941e"><span data-lake-id="u490f5023" id="u490f5023">kill命令默认的信号就是15，首先来说一下这个默认的</span><span data-lake-id="uc3f56c96" id="uc3f56c96">kill -15</span><span data-lake-id="u47bcf654" id="u47bcf654">信号。</span></p>
  <p data-lake-id="u869a516c" id="u869a516c"><span data-lake-id="u24c4b1ec" id="u24c4b1ec">当使用kill -15时，系统会发送一个SIGTERM的信号给对应的程序。当程序接收到该信号后，具体要如何处理是自己可以决定的。这时候，应用程序可以选择：</span></p>
  <ul list="u7674c747">
   <li fid="uac11441f" data-lake-id="u81ac9dbd" id="u81ac9dbd"><span data-lake-id="u22999080" id="u22999080">立即停止程序</span></li>
   <li fid="uac11441f" data-lake-id="u0cc676ad" id="u0cc676ad"><span data-lake-id="u074be387" id="u074be387">释放响应资源后停止程序</span></li>
   <li fid="uac11441f" data-lake-id="ue5a2be61" id="ue5a2be61"><span data-lake-id="u5326a598" id="u5326a598">忽略该信号，继续执行程序</span></li>
  </ul>
  <p data-lake-id="u4d748bc5" id="u4d748bc5"><span data-lake-id="u8e18c3b5" id="u8e18c3b5">因为</span><span data-lake-id="uaa3f0185" id="uaa3f0185">kill -15</span><span data-lake-id="uecf77f94" id="uecf77f94">信号只是通知对应的进程要进行”安全、干净的退出”，程序接到信号之后，退出前一般会进行一些”准备工作”，如资源释放、临时文件清理等等，如果准备工作做完了，再进行程序的终止。</span></p>
  <p data-lake-id="ue820ff42" id="ue820ff42"><span data-lake-id="u8a81fdd2" id="u8a81fdd2">但是，如果在”准备工作”进行过程中，遇到阻塞或者其他问题导致无法成功，那么应用程序可以选择忽略该终止信号。</span></p>
  <p data-lake-id="u696356be" id="u696356be"><span data-lake-id="u5f069735" id="u5f069735">这也就是为什么我们有的时候使用kill命令是没办法”杀死”应用的原因，因为默认的kill信号是</span><code data-lake-id="u459492e2" id="u459492e2"><span data-lake-id="uafd98ea1" id="uafd98ea1">SIGTERM（15）</span></code><span data-lake-id="u3daf9eff" id="u3daf9eff">，而</span><code data-lake-id="u78267e5c" id="u78267e5c"><span data-lake-id="ue1c9fb43" id="ue1c9fb43">SIGTERM（15）</span></code><span data-lake-id="u4a5d45b0" id="u4a5d45b0">的信号是可以被阻塞和忽略的。</span></p>
  <p data-lake-id="u5dcbcf46" id="u5dcbcf46"><span data-lake-id="u8359be44" id="u8359be44">和</span><code data-lake-id="ub5a3bc63" id="ub5a3bc63"><span data-lake-id="u14b4cff7" id="u14b4cff7">kill -15</span></code><span data-lake-id="ubef3b99f" id="ubef3b99f">相比，</span><code data-lake-id="uca151e68" id="uca151e68"><span data-lake-id="ub2107655" id="ub2107655">kill -9</span></code><span data-lake-id="u0a810ad3" id="u0a810ad3">就相对强硬一点，系统会发出SIGKILL信号，他要求接收到该信号的程序应该立即结束运行，不能被阻塞或者忽略。</span></p>
  <p data-lake-id="u0f623454" id="u0f623454"><span data-lake-id="u70fb6bd6" id="u70fb6bd6">所以，相比于</span><code data-lake-id="u40ffce71" id="u40ffce71"><span data-lake-id="u24a8faab" id="u24a8faab">kill -15</span></code><span data-lake-id="uf37c4283" id="uf37c4283">命令，</span><code data-lake-id="u301189cc" id="u301189cc"><span data-lake-id="ucd53f6f4" id="ucd53f6f4">kill -9</span></code><span data-lake-id="u2c600bef" id="u2c600bef">在执行时，应用程序是没有时间进行”准备工作”的，所以</span><strong><span data-lake-id="u428cc4d0" id="u428cc4d0">这通常会带来一些副作用，数据丢失或者终端无法恢复到正常状态等</span></strong><span data-lake-id="uec2764e9" id="uec2764e9">。</span></p>
  <h1 data-lake-id="Oo3sL" id="Oo3sL"><span data-lake-id="u100c62a6" id="u100c62a6">知识扩展</span></h1>
  <h2 data-lake-id="rOV9X" id="rOV9X"><span data-lake-id="u8aa42328" id="u8aa42328">Java是如何处理SIGTERM(15)的</span></h2>
  <p data-lake-id="u350c288a" id="u350c288a"><span data-lake-id="u5debd546" id="u5debd546">我们都知道，在Linux中，Java应用是作为一个独立进程运行的，Java程序的终止运行是基于JVM的关闭实现的，JVM关闭方式分为3种：</span></p>
  <ol list="uc7aa7347">
   <li fid="u09429c99" data-lake-id="u5d253f74" id="u5d253f74"><span data-lake-id="ud3e91a29" id="ud3e91a29">正常关闭：当最后一个非守护线程结束或者调用了System.exit或者通过其他特定平台的方法关闭（接收到SIGINT（2）、SIGTERM（15）信号等）</span></li>
   <li fid="u09429c99" data-lake-id="uf3edb5ad" id="uf3edb5ad"><span data-lake-id="ub271e1aa" id="ub271e1aa">强制关闭：通过调用Runtime.halt方法或者是在操作系统中强制kill（接收到SIGKILL（9）信号)</span></li>
   <li fid="u09429c99" data-lake-id="ue3c0ec6e" id="ue3c0ec6e"><span data-lake-id="ub4bb21c2" id="ub4bb21c2">异常关闭：运行中遇到RuntimeException异常等。</span></li>
  </ol>
  <p data-lake-id="u5348ce28" id="u5348ce28"><span data-lake-id="ueae8c859" id="ueae8c859">JVM进程在接收到</span><span data-lake-id="u8da88acf" id="u8da88acf">kill -15</span><span data-lake-id="u82107df8" id="u82107df8">信号通知的时候，是可以做一些清理动作的，比如删除临时文件等。</span></p>
  <p data-lake-id="u5e829c6c" id="u5e829c6c"><span data-lake-id="ub810858e" id="ub810858e">当然，开发者也是可以自定义做一些额外的事情的，比如让tomcat容器停止，让dubbo服务下线等。</span></p>
  <p data-lake-id="u0bc60227" id="u0bc60227"><span data-lake-id="u4cec02e2" id="u4cec02e2">而这种自定义JVM清理动作的方式，是通过JDK中提供的shutdown hook实现的。JDK提供了</span><code data-lake-id="ud2cb380f" id="ud2cb380f"><span data-lake-id="uaa244618" id="uaa244618">Java.Runtime.addShutdownHook(Thread hook)</span></code><span data-lake-id="u41d9b3ae" id="u41d9b3ae">方法，可以注册一个JVM关闭的钩子。</span></p>
  <p data-lake-id="u0d11cbaa" id="u0d11cbaa"><span data-lake-id="u5711ed3b" id="u5711ed3b">例子如下：</span></p>
  <pre lang="java"><code>
public class ShutdownHookTest {

    public static void main(String[] args) {
        boolean flag = true;
        Runtime.getRuntime().addShutdownHook(new Thread(() -&gt; {
            System.out.println("hook execute...");
        }));

        while (flag) {
            // app is runing
        }

        System.out.println("main thread execute end...");
    }
}
</code></pre>
  <p data-lake-id="ucc2f2de2" id="ucc2f2de2"><span data-lake-id="u93de89be" id="u93de89be">执行命令：</span></p>
  <pre lang="java"><code>
➜ jps
6520 ShutdownHookTest
6521 Jps
➜ kill 6520
</code></pre>
  <p data-lake-id="ua6201b77" id="ua6201b77"><span data-lake-id="u381efffa" id="u381efffa">控制台输出内容：</span></p>
  <p data-lake-id="ub617389f" id="ub617389f"><code data-lake-id="u349eba9a" id="u349eba9a"><span data-lake-id="uf4885e47" id="uf4885e47">hook execute... Process finished with exit code 143 (interrupted by signal 15: SIGTERM)</span></code></p>
  <p data-lake-id="uf55d42d2" id="uf55d42d2"><span data-lake-id="u72bcbfe3" id="u72bcbfe3">可以看到，当我们使用kill（默认kill -15）关闭进程的时候，程序会先执行我注册的shutdownHook，然后再退出，并且会给出一个提示：interrupted by signal 15: SIGTERM</span></p>
  <p data-lake-id="u25be00ad" id="u25be00ad"><span data-lake-id="ufd52a39e" id="ufd52a39e">如果我们执行命令kill -9：</span></p>
  <pre lang="java"><code>
➜ kill -9 6520
</code></pre>
  <p data-lake-id="u592b3132" id="u592b3132"><span data-lake-id="ufc1a0617" id="ufc1a0617">控制台输出内容：</span></p>
  <p data-lake-id="u1bd1e905" id="u1bd1e905"><code data-lake-id="u3aa9e696" id="u3aa9e696"><span data-lake-id="u9e2ae558" id="u9e2ae558">Process finished with exit code 137 (interrupted by signal 9: SIGKILL)</span></code></p>
  <p data-lake-id="ubdfc2521" id="ubdfc2521"><span data-lake-id="uaa950bf1" id="uaa950bf1">可以看到，当我们使用kill -9 强制关闭进程的时候，程序并没有执行shutdownHook，而是直接退出了，并且会给出一个提示：interrupted by signal 9: SIGKILL</span></p>
  <h2 data-lake-id="fXmSM" id="fXmSM"><span data-lake-id="u3f0457f8" id="u3f0457f8">正常的重启机器流程是什么样的</span></h2>
  <ol list="u7f0e5782">
   <li fid="u699e6761" data-lake-id="u43d17720" id="u43d17720"><span data-lake-id="u6b864336" id="u6b864336">先将堆栈的文件dump下来</span></li>
   <li fid="u699e6761" data-lake-id="ub6328812" id="ub6328812"><span data-lake-id="ucd76b221" id="ucd76b221">重启机器，如果重启失败，则采用kill -15命令</span></li>
   <li fid="u699e6761" data-lake-id="uaec5ab8f" id="uaec5ab8f"><span data-lake-id="u42b5db56" id="u42b5db56">事后分析dump文件</span></li>
  </ol>
  <p data-lake-id="ud36b7c65" id="ud36b7c65"><span data-lake-id="u2aa73020" id="u2aa73020">​</span><br></p>
  <blockquote data-lake-id="u645a6ee6" id="u645a6ee6">
   <p data-lake-id="u4414a70a" id="u4414a70a"><span data-lake-id="u25fd9066" id="u25fd9066">重启的过程是，先stop、再start。很多时候，start失败的概率要比stop失败的概率低多了。</span></p>
   <p data-lake-id="ue0edf133" id="ue0edf133"><span data-lake-id="u2b575f1a" id="u2b575f1a">当stop失败的时候，就有进程在，可以对他用kill -15</span></p>
  </blockquote>
 </body>
</html>